﻿using System;
using System.Collections.Generic;
using System.Text;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Input;
using Rites.GameObjects;
using Rites.Networking;

namespace Rites
{
    public class Game
    {
        public event TextOutputDelegate ShowText;
        public eGameState gameState;

        public Player LocalPlayer { get; set; } //Direct reference to local player
        public Dictionary<int, Player> _players = new Dictionary<int, Player>(); //Indexed list of players, including local player
        string _receiveBuffer = String.Empty;

        //Networking internals
        public long LastSendMessageID;

        //Events to call when the client received data or got disconnected
        public Connection riteConn;

        public Game()
        {
            gameState = eGameState.Offline;

            riteConn = new Connection();
            riteConn.DataReceived += new DataReceivedDlg(DataReceivedFromServer);
            riteConn.UserDisconnected += new UserEventDlg(YouHaveBeenDisconnected);
        }

        private void YouHaveBeenDisconnected(object sender)
        {
            ShowText("Disconnected from server.");
        }

        private void DataReceivedFromServer(byte[] data)
        {
            try
            {
                string rawBuffer = System.Text.Encoding.ASCII.GetString(data);
                _receiveBuffer += rawBuffer;

                int messageStart = _receiveBuffer.IndexOf(Constants.BOM);
                int messageStop = _receiveBuffer.IndexOf(Constants.EOM);

                while (messageStart >= 0 && messageStop >= 0)
                {
                    string receivedData = _receiveBuffer.Substring(messageStart + Constants.BOM.Length, messageStop - (messageStart + Constants.BOM.Length));
                    _receiveBuffer = _receiveBuffer.Replace(Constants.BOM + receivedData + Constants.EOM, "");
                    ProcessMessageFromServer(receivedData);
                    messageStart = _receiveBuffer.IndexOf(Constants.BOM);
                    messageStop = _receiveBuffer.IndexOf(Constants.EOM);
                }

                //Clear the receive buffer if no partial messages found
                if (messageStart == -1)
                    _receiveBuffer = string.Empty;
            }
            catch (Exception ex)
            {
                ShowText(ex.ToString());
            }
        }

        private void ProcessMessageFromServer(string serverMessage)
        {
            string[] metaParams = serverMessage.Split('~');
            long messageID = long.Parse(metaParams[1]);
            string[] dataParams = metaParams[2].Split('#');

            //Console.WriteLine("Message type: " + System.Enum.GetName(typeof(RitesProtocol), int.Parse(metaParams[0])) + ": " + metaParams[2]);
            //Util.Log("Message type: " + System.Enum.GetName(typeof(RitesProtocol), int.Parse(metaParams[0])));
            //Util.Log("Message ID: " + metaParams[1]);
            //Util.Log("Parameters: " + metaParams[2]);

            switch ((RitesProtocol)(int.Parse(metaParams[0])))
            {
                case RitesProtocol.PlayerID:    //Server has assigned us an ID. It's our precioussss....
                    LocalPlayer.ID = int.Parse(dataParams[0]);
                    _players.Add(LocalPlayer.ID, LocalPlayer);
                    ShowText("ID received from server: " + LocalPlayer.ID);
                    ShowText("Sending this player information");
                    riteConn.SendMessage(RitesProtocol.PlayerUpdate, LocalPlayer.Name); //Send local player info to server - will need bulletproof validation
                    riteConn.SendMessage(RitesProtocol.PlayerList, ""); //Request a playerlist from the server
                    break;
                case RitesProtocol.PlayerList:
                    if (metaParams[2].Length == 0)
                        ShowText("You sneak into the poorly lit bar. You don't see anyone unusual.");
                    else
                    {
                        ShowText("Some people are already here... hidden in the shadows");
                        for (int index = 0; index < dataParams.Length; index++)
                        {
                            Player addPlr = new Player();
                            addPlr.ID = int.Parse(dataParams[index]);
                            _players.Add(addPlr.ID, addPlr);
                        }
                    }
                    break;
                case RitesProtocol.Chat:
                    ShowText(_players[int.Parse(dataParams[0])].Name + ": " + dataParams[1]);
                    break;
                case RitesProtocol.UserConnected:
                    Player newPlr = new Player();
                    newPlr.ID = int.Parse(dataParams[0]);
                    _players.Add(newPlr.ID, newPlr);
                    ShowText("A shadowy figure has entered the dimly lit room");
                    break;
                case RitesProtocol.PlayerUpdate:
                    _players[int.Parse(dataParams[0])].Name = dataParams[1];
                    break;
                case RitesProtocol.UserDisconnected:
                    ShowText(_players[int.Parse(dataParams[0])].Name + " fades away.");
                    _players.Remove(int.Parse(dataParams[0]));
                    break;
                default:
                    ShowText("Error -- Unhandled message type from server: " + System.Enum.GetName(typeof(RitesProtocol), int.Parse(metaParams[0])));
                    break;
            }
        }

        public void TextEvent(RitesProtocol msgType, string messageString)
        {
            switch (gameState)
            {
                case eGameState.Offline:
                    ShowText("No commands while offline. It's the rules.");
                    break;
                case eGameState.GettingIP:
                    if (ConnectToServer(messageString))
                        gameState = eGameState.Lobby;
                    else
                    {
                        ShowText("Unable to connect.");
                        gameState = eGameState.Offline;
                    }
                    break;
                case eGameState.Lobby:
                    riteConn.SendMessage(msgType, messageString);
                    break;
            }
        }

        public bool ConnectToServer(string sIP)
        {
            return riteConn.Connect(sIP, Constants.PORT);
        }

        public void HandleInput(InputHandler inputHandler)
        {
            if (inputHandler.KeyPressed(Keys.Escape))
                ExitGame();

            inputHandler.FlushBuffer();
        }

        public void ExitGame()
        {
            if (riteConn.Connected)
            {
                riteConn.SendMessage(RitesProtocol.UserDisconnected, "");
                riteConn.Disconnect();
            }

            gameState = eGameState.Quitting;
        }

        //Monsters move here etc.
        public void Update(GameTime gameTime)
        {
            switch (gameState)
            {
                case eGameState.Offline:
                    ShowText("Please enter IP address of server:");
                    gameState = eGameState.GettingIP;
                    break;
                case eGameState.Lobby:
                    break;
            }
        }
    }
}